package 多线程.并发编程;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

/*
【薛定谔的FutureTask】
为什么说是薛定谔的FutureTask呢？那是因为，当你把FutureTask跑起来的时候，里面的outcome可能没有值，也可能有值。

但是又因为outcome在FutureTask源码中被设置成private，所以如果你要获取这个数据，只能通过get方法。而get方法是阻塞的，当你调用get方法时，
一定是等到任务执行成功后，才会返回真实的值。

这就有点像薛定谔的猫，你不去观察它，两种状态皆有可能，一旦你去观察了（调用get方法），就只有一种明确的状态。

其实这真的只是一个小技巧，相信你也能办到，我们用代码来模拟一下这个过程。

首先，新建一个MyFutureTask类：
 */
/*
public class Test {
	public static void main(String[] args) throws InterruptedException {
		MyExecutorService myExecutorService = new MyExecutorService();
		MyFutureTask myFutureTask = myExecutorService.submit(new MyFutureTask());
		// 线程开启立刻查看outcome
		System.out.println("start: "+myFutureTask.outcome);
		// 主线程继续运作
		Thread.sleep(1100);
		// 再次查看outcome是否有值
		//System.out.println(myFutureTask.get());
		System.out.println("end: "+myFutureTask.outcome);
	}

}

//自定义任务类
class MyFutureTask implements Runnable {
	// 为了看到效果，outcome设置为Object
	public Object outcome;

	public void run() {
		try {
			// 执行耗时操作
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 给outcome赋值
		this.outcome = "SUCCESS";
	}

	public Object get() {
		return outcome;
	}

}

class MyExecutorService {
	// 提交任务
	// @param myFutureTask
	// @return
	public MyFutureTask submit(MyFutureTask myFutureTask) {
		// 开启一个线程把myFutureTask跑掉
		new Thread(myFutureTask).start();
		// 线程有没有跑完不关心，直接把myFutureTask返回,此时myFutureTask很可能不是最终结果，但其中的outcome一定指向最终结果
		return myFutureTask;
	}
}
*/
/*
结果：
null
SUCCESS
总结一下，ExecutorService的submit方法只是提交Runnable或Callable任务到线程池，直接返回FutureTask给你，这个FutureTask是薛定谔的FutureTask，
里面的outcome现在可能有值，也可能没有。只有当你主动调用get方法，才可以得到确切的值。
*/
import java.util.concurrent.locks.LockSupport;
import java.util.function.BiConsumer;
import java.util.function.Supplier;

/*
【FutureTask的get方法阻塞原理】
FutureTask的get方法是阻塞的，当你调用这个方法就一定要等该线程跑完，那么为什么能做到这样呢？

接下来我们看看get方法的阻塞原理是什么，我重新写了一个例子，注释能写的都写了，代码如下：
 */
/*
class MyExecutorService {
    //提交任务
    public MyFutureTask submit(MyFutureTask myFutureTask){
        //开启一个线程把myFutureTask跑掉
        Thread thread = new Thread(myFutureTask);
        thread.start();
        //把线程赋给myFutureTask的runner属性，以方便查看线程状态
        myFutureTask.runner = thread;
        //线程有没有跑完不关心，直接把myFutureTask返回,此时myFutureTask很可能不是最终结果，但其中的outcome一定指向最终结果
        return myFutureTask;
    }
}

//自定义任务类
class MyFutureTask implements Runnable{ 
    public Object outcome;//为了看到效果，outcome设置为Object
    public volatile Thread runner;//共享变量，当前任务所在的线程
    public void run(){
        try {
            Thread.sleep(1000);//执行耗时操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.outcome = "SUCCESS";//给outcome赋值
    }

    public Object get() throws InterruptedException {
        if(awaitDone())
            return outcome;
        return null;
    }

    private boolean awaitDone() throws InterruptedException {
        //做一个死循环，轮询检查当前线程状态
        for(;;){           
            if(runner.isInterrupted()){//如果当前线程被打断，则抛异常结束任务
                throw new InterruptedException();
            }
            if(runner.getState() == Thread.State.NEW){
                System.out.println(Thread.currentThread().getName() + "新建！");
            }
            if(runner.getState() == Thread.State.RUNNABLE){
                System.out.println(Thread.currentThread().getName() + "准备就绪！");
            }

            if(runner.getState() == Thread.State.TERMINATED){
                System.out.println(Thread.currentThread().getName() + "执行完毕！");
                return true;//执行完成，跳出循环
            }
            Thread.sleep(300);
        }
    }
}
public class Test{
    public static void main(String[] args) throws InterruptedException {
        MyExecutorService myExecutorService = new MyExecutorService();
        MyFutureTask myFutureTask = myExecutorService.submit(new MyFutureTask());
        
        System.out.println(myFutureTask.outcome);//线程开启立刻查看outcome
        //主线程继续运作
        //Thread.sleep(1100);
        //再次查看outcome是否有值,现在是阻塞的
        System.out.println(myFutureTask.get());
        
    }
}
*/
/*
null
main准备就绪！
main执行完毕！
SUCCESS

关键就在于这个 awaitDone 方法（源码也叫这个名字），它里面是一个死循环，不断去检查当前FutureTask所在线程的状态，当线程执行结束，就返回true，表示可以
给出精确的result了。

真实的get方法实现非常复杂，不过思路是差不多的，有兴趣的童鞋可以去百度了解。
*/
/*
public class Test{
   public static void main(String[] args) throws ExecutionException,InterruptedException {
   	
   	//FutureTask包装Runnable（通过构造器）public FutureTask(Runnable runnable, V result)
   	FutureTask<String> runnable=new FutureTask<String>(new Runnable() {		
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName()+"========>正在执行");				
			}
		},"success");
   
   	//FutureTask包装Callable（通过构造器）public FutureTask>(Callable<String> callable)
   	FutureTask<String> callable=new FutureTask<String>(new Callable<>() {
   		@Override
   		public String call() throws Exception{
   			try {
   				Thread.sleep(3*1000L);
   			} catch (InterruptedException e) {
   				e.printStackTrace();
   			}
   			return "success";
   		}
   	});
   
   	System.out.println(Thread.currentThread().getName() + "========>启动任务");
   	new Thread(runnable).start();
   	String result = runnable.get();
      System.out.println("任务执行结束，result====>" + result);
      
      new Thread(callable).start();
      System.out.println("任务执行结束，result====>" + callable.get());
   }
}
*/
/*
public class TestT {
   @Test
   public void testPark() throws InterruptedException {
       List<Thread> threadList = new ArrayList<>();// 存储线程
       // 创建5个线程
       for (int i = 0; i < 5; i++) {
           Thread thread = new Thread(() -> {
               System.out.println("我是" + Thread.currentThread().getName() + ", 我开始工作了~");
               //LockSupport.park(Thread.currentThread());
               LockSupport.park(this);
               System.out.println("我是" + Thread.currentThread().getName() + ", 我又活过来了~");
           });
           thread.start();
           threadList.add(thread);
       }

       Thread.sleep(3 * 1000L);
       System.out.println("====== 所有线程都阻塞了，3秒后全部恢复了 ======");

       // unPark()所有线程
       for (Thread thread : threadList) {
           LockSupport.unpark(thread);
       }
       Thread.sleep(3 * 1000L);// 等所有线程执行完毕
   }
}
*/

public class TestT {
	@Test
	public void testCallBack() throws InterruptedException, ExecutionException {
		// 提交一个任务，返回CompletableFuture（注意，并不是把CompletableFuture提交到线程池，它没有实现Runnable）
		CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(new Supplier<String>() {
			@Override
			public String get() {
				System.out.println("=============>异步线程开始...");
				System.out.println("=============>异步线程为：" + Thread.currentThread().getName());
				try {
					TimeUnit.SECONDS.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("=============>异步线程结束...");
				return "supplierResult";
			}
		});

		// 异步回调：上面的Supplier#get()返回结果后，异步线程会回调BiConsumer#accept()
		completableFuture.whenComplete(new BiConsumer<String, Throwable>() {
			@Override
			public void accept(String s, Throwable throwable) {
				System.out.println("=============>异步任务结束回调...");
				System.out.println("=============>回调线程为：" + Thread.currentThread().getName());
			}
		});

		// CompletableFuture的异步线程是守护线程，一旦main结束就没了，为了看到打印结果，需要让main休眠一会儿
		System.out.println("main结束");
		TimeUnit.SECONDS.sleep(11);
	}
}